# Check requisite packages are installed.
packages <- c(
  "plotly", 
  "dplyr",
  "RMTRCode2"
)
for (pkg in packages) {
  library(pkg, character.only = TRUE)
}
package 㤼㸱plotly㤼㸲 was built under R version 4.0.5package 㤼㸱ggplot2㤼㸲 was built under R version 4.0.3package 㤼㸱dplyr㤼㸲 was built under R version 4.0.4
# Reserved Names
candidateData <- NULL
islandInteractionsOneEmptyTwoWhich <- NULL
islandInteractionsOneTwo <- NULL
islandInteractionsOneTwoWhich <- NULL
mats <- NULL
paramFrame <- NULL
plotScalingData <- NULL
pools <- NULL

Disentangling Effects on the Viking Data

Load Data

ellipsisApply <- function(..., FUN) {
  lapply(as.list(...), FUN)
}

load("LM1996-NumPoolCom-QDat-2021-05.RData")
# Stop if not all are not null
stopifnot(all(unlist(ellipsisApply(
  FUN = function(bool) {!is.null(bool)},
  candidateData, 
  islandInteractionsOneEmptyTwo,
  islandInteractionsOneEmptyTwoWhich,
  islandInteractionsOneTwo,
  islandInteractionsOneTwoWhich,
  mats,
  paramFrame,
  plotScalingData,
  pools
))))
plotScaling <- plotly::plot_ly(
  plotScalingData,
  x = ~Basals,
  y = ~Consumers,
  z = ~CommunitySize,
  color = ~Dataset,
  colors = c("red", "blue", "black")
)

plotScaling <- plotly::add_markers(plotScaling)

plotScaling <- plotly::layout(
  plotScaling,
  scene = list(
    xaxis = list(type = "log"),
    yaxis = list(type = "log"),
    camera = list(
      eye = list(
        x = -1.25, y = -1.25, z = .05
      )
    )
  )
)

plotScaling
# Check that the Two island and Three island scenarios are set-up the same.
stopifnot(unlist(lapply(islandInteractionsOneTwo, length)) == 
            unlist(lapply(islandInteractionsOneEmptyTwo, length)))
stopifnot(names(islandInteractionsOneTwo) == 
            names(islandInteractionsOneEmptyTwo))
# Check that the Which versions correspond correctly.
stopifnot(
  unlist(lapply(islandInteractionsOneTwoWhich, function(x) {
    length(RMTRCode2::CsvRowSplit(x))
  })) 
  == unlist(lapply(islandInteractionsOneTwo, function(x) {
    # We're like onions; we have LAYERS!
    lapply(x, function(y) {
      lapply(y, function(z) {
        sum(z > 1E-6) # How many "large" entries are there?
      })})}))
)
stopifnot(
  unlist(lapply(islandInteractionsOneEmptyTwoWhich, function(x) {
    length(RMTRCode2::CsvRowSplit(x))
  })) 
  == unlist(lapply(islandInteractionsOneEmptyTwo, function(x) {
    # We're like onions; we have LAYERS!
    lapply(x, function(y) {
      lapply(y, function(z) {
        sum(z > 1E-6) # How many "large" entries are there?
      })})}))
)
# Hybrids
# Create a count of how many times each entry will be repeated.
communitiesAllRepeater <- 5 * unlist(lapply(islandInteractionsOneTwo, length))

# Create template.
communitiesAll <- data.frame(
  CombnNum = rep(0, sum(communitiesAllRepeater)), # Should repeat all rows.
  Basals = 0,
  Consumers = 0,
  Dataset = "",
  DatasetID = 0,
  Communities = "",
  CommunitySize = 0,
  OtherSteadyStates = 0, # To be recalculated
  CommunityAbund = "",
  CommunityProd = 0,
  TotalID = "",
  # Additional Column!, 1 for direct assembly, 0 unused.
  IslandsUsed = rep(c(2,2,3,3,3), sum(communitiesAllRepeater)/5)
)

# Retrieve the rows used to make hybrids
communitiesAllProspects <- candidateData %>% dplyr::group_by(
  CombnNum, Basals, Consumers, Dataset, DatasetID, TotalID
) %>% dplyr::select(
  CombnNum:DatasetID, TotalID
) %>% dplyr::summarise(
  Count = dplyr::n(), .groups = "keep"
) %>% dplyr::filter(
  Count > 1
) %>% dplyr::select(
  -Count
) %>% dplyr::arrange(
  DatasetID, CombnNum
)

# Make sure that the names match.
stopifnot(communitiesAllProspects$TotalID == names(communitiesAllRepeater))

# Insert repetitions.
communitiesAll$CombnNum <- rep(communitiesAllProspects$CombnNum, communitiesAllRepeater)
communitiesAll$Basals <- rep(communitiesAllProspects$Basals, communitiesAllRepeater)
communitiesAll$Consumers <- rep(communitiesAllProspects$Consumers, communitiesAllRepeater)
communitiesAll$Dataset <- rep(communitiesAllProspects$Dataset, communitiesAllRepeater)
communitiesAll$DatasetID <- rep(communitiesAllProspects$DatasetID, communitiesAllRepeater)
communitiesAll$TotalID <- rep(communitiesAllProspects$TotalID, communitiesAllRepeater)

# To move over from the data.
# Communities = "",
# CommunityAbund = ""

communitiesAll[communitiesAll$IslandsUsed == 2, ]$Communities <- 
  islandInteractionsOneTwoWhich
communitiesAll[communitiesAll$IslandsUsed == 3, ]$Communities <- 
  islandInteractionsOneEmptyTwoWhich

communitiesAll[communitiesAll$IslandsUsed == 2, ]$CommunityAbund <- 
  # We're like onions; we have LAYERS!
  unlist(lapply(islandInteractionsOneTwo, function(x) {
    lapply(x, function(y) {
      lapply(y, function(z) {
        toString(z[z > 1E-6])
      })
    })
  }))
  
communitiesAll[communitiesAll$IslandsUsed == 3, ]$CommunityAbund <- 
  unlist(lapply(islandInteractionsOneEmptyTwo, function(x) {
    lapply(x, function(y) {
      lapply(y, function(z) {
        toString(z[z > 1E-6])
      })
    })
  }))

# To calculate from the data.
# CommunitySize = 0, # To be calculated from Communities.
# OtherSteadyStates = 0, # To be recalculated last after filtering
# CommunityProd = 0, # To be recalculated after Abund stored.
communitiesAll$CommunitySize <- unlist(lapply(
  communitiesAll$Communities, function(x) {
    length(RMTRCode2::CsvRowSplit(x))
  })) 

for (r in 1:nrow(communitiesAll)) {
  communitiesAll$CommunityProd[r] <- with(
    communitiesAll[r, ], 
    RMTRCode2::Productivity(
      Pool = pools[[DatasetID]][[CombnNum]], 
      InteractionMatrix = mats[[DatasetID]][[CombnNum]], 
      Community = Communities, 
      Populations = CommunityAbund
    )
  )
}
# Original systems
communitiesAll <- rbind(
  candidateData %>% dplyr::select(
    -CommunityFreq, -CommunitySeq
  ) %>% dplyr::mutate(
    IslandsUsed = 1
  ), 
  communitiesAll
)
# Treating the Productivity like one might treat a hash,
# if two rows with the same properties are assigned the same hash, 
# we only keep one. 
# One decimal place might be excessive, 
# but we can reflect on that if results down the line are not interesting.
# For the record though, it appears that it is a decently good approach at 
# removing effectively numerical duplicates.
# Not bothering, sort of, with IslandsUsed, since many times a community is
# reproduced on varying numbers of islands.

# communitiesAll <- communitiesAll %>% dplyr::mutate(
#   tempProd = round(CommunityProd, 2)
# ) %>% dplyr::distinct(
#   CombnNum, Basals, Consumers, Dataset, DatasetID, TotalID,
#   Communities, CommunitySize, tempProd, IslandsUsed,
#   .keep_all = TRUE
# ) %>% dplyr::select(
#   -tempProd
# )

communitiesAll <- communitiesAll %>% dplyr::mutate(
  tempProd = round(CommunityProd, 2)
) %>% dplyr::group_by(
  CombnNum, Basals, Consumers, Dataset, DatasetID, TotalID,
  Communities, CommunitySize, tempProd,
) %>% dplyr::summarise(
  CommunityAbund = dplyr::first(CommunityAbund),
  CommunityProd = dplyr::first(CommunityProd),
  IslandsUsed = toString(unique(IslandsUsed)),
  .groups = "drop"
) %>% dplyr::select(
  -tempProd
) %>% dplyr::group_by(
  CombnNum, Basals, Consumers, Dataset, DatasetID, TotalID
) %>% dplyr::mutate(
  OtherSteadyStates = dplyr::n() - 1,
  Islands1 = grepl(pattern = "1", IslandsUsed, fixed = TRUE) # Will be useful
)

Persistence of Hybrid Communities

The idea is straightforward: after allowing interactions between islands, for islands that are not the same as one of the original communities, isolate the island and check to see what happens.

communitiesHybrids <- communitiesAll %>% dplyr::filter(
  !Islands1
) %>% dplyr::select(-Islands1)
communitiesHybrids$AfterSepAbund <- ""
communitiesHybrids$AfterSepCommunity <- ""
communitiesHybrids$AfterSepCommunitySize <- 0
communitiesHybrids$AfterSepProduction <- 0
for (r in 1:nrow(communitiesHybrids)) {
  temp <- with(
    communitiesHybrids[r, ],
    {    
      temp <- RMTRCode2::CsvRowSplit(Communities)
      RMTRCode2::LawMorton1996_NumIntegration(
        A = mats[[DatasetID]][[CombnNum]][temp, temp],
        R = pools[[DatasetID]][[CombnNum]]$ReproductionRate[temp],
        X = RMTRCode2::CsvRowSplit(CommunityAbund), 
        OuterTimeStepSize = 3E4,
        Tolerance = 1E-6
      ) # retrieve the abundance over time matrix
    }
  ) 
  
  temp <- temp[nrow(temp), -1] # choose last row, remove time column.
  
  communitiesHybrids$AfterSepCommunity[r] <- toString(
    RMTRCode2::CsvRowSplit(communitiesHybrids$Communities[r])[which(temp > 1E-6)]
  )
  
  temp <- temp[which(temp > 1E-6)] # remove microfoxes.
  
  communitiesHybrids$AfterSepAbund[r] <- toString(temp)
  communitiesHybrids$AfterSepCommunitySize[r] <- length(temp)
  
  communitiesHybrids$AfterSepProduction[r] <- with(
    communitiesHybrids[r, ], 
    RMTRCode2::Productivity(
      Pool = pools[[DatasetID]][[CombnNum]], 
      InteractionMatrix = mats[[DatasetID]][[CombnNum]], 
      Community = AfterSepCommunity, 
      Populations = AfterSepAbund
    )
  )
}
communitiesHybrids <- communitiesHybrids %>% dplyr::mutate(
  Persists = AfterSepCommunity == Communities,
  ProdChange = AfterSepProduction - CommunityProd
)

So after running the dynamics for 3E4 time units (i.e. 3x the length of time the dynamics in the numerical assembly runs in between assembly steps and 1.5x the length of the island dynamics), the communities that persist are 6, 7, 8, 9, 10. Examining the communities themselves, they are all the same community, albeit with different starting points.

communitiesHybrids[communitiesHybrids$Persists, ]

An obvious follow-up question: how many of the communities that collapse do so to communities that we have not already seen?

communitiesHybrids <- communitiesHybrids %>% dplyr::mutate(
  AfterSepCommunityAlreadyPresent = AfterSepCommunity %in% communitiesAll$Communities
)
sum(!communitiesHybrids$AfterSepCommunityAlreadyPresent)
[1] 12

Consolidating down to unique ending states we have the following.

communitiesHybrids[
  !communitiesHybrids$AfterSepCommunityAlreadyPresent, 
  ] %>% dplyr::distinct(CombnNum, AfterSepCommunity, .keep_all = TRUE)

We will add these new states to our catalogue of communities from the experiments. We also take the abundance after separation if the community persists to better reflect steady-state conditions.

communitiesAll <- rbind(
  communitiesAll %>% dplyr::filter(
    Islands1 == TRUE
  ) %>% dplyr::mutate(
    HybridCollapse = FALSE, Persists = TRUE
  ),
  communitiesHybrids %>% dplyr::mutate(
    CommunityAbund = ifelse(Persists, AfterSepAbund, CommunityAbund),
    Islands1 = FALSE, HybridCollapse = FALSE,
  ) %>% dplyr::select(
    -AfterSepAbund, -AfterSepCommunity, -AfterSepCommunitySize, 
    -AfterSepProduction, -ProdChange, -AfterSepCommunityAlreadyPresent
  ) ,
  with(
    communitiesHybrids[
      !communitiesHybrids$AfterSepCommunityAlreadyPresent, 
    ] %>% dplyr::distinct(CombnNum, AfterSepCommunity, .keep_all = TRUE),
    data.frame(
      CombnNum = CombnNum,
      Basals = Basals,
      Consumers = Consumers,
      Dataset = Dataset,
      DatasetID = DatasetID,
      TotalID = TotalID,
      Communities = AfterSepCommunity,
      CommunitySize = AfterSepCommunitySize,
      CommunityAbund = AfterSepAbund,
      CommunityProd = AfterSepProduction,
      IslandsUsed = IslandsUsed,
      OtherSteadyStates = 0,
      Islands1 = FALSE,
      HybridCollapse = TRUE,
      Persists = TRUE,
      stringsAsFactors = FALSE
    ))
)

Invadability of Hybrid Communities

Looking at a longer time scale, what happens if/when invasions resume? Do the hybrid communities that emerged retain the uninvadability of the parent communities?

This question should be straightforward as it is testing a step from the assembly process.

communitiesAll$Uninvadable <- NA
for (r in 1:nrow(communitiesAll)) {
  communitiesAll$Uninvadable[r] <- with(
    communitiesAll[r, ],
    {
      tempRow <- rep(NA, nrow(pools[[DatasetID]][[CombnNum]]) + 1)
      tempRow[RMTRCode2::CsvRowSplit(Communities) + 1] <- 
        RMTRCode2::CsvRowSplit(CommunityAbund)
      RMTRCode2::LawMorton1996_CheckUninvadable(
        AbundanceRow = tempRow,
        Pool = pools[[DatasetID]][[CombnNum]],
        CommunityMatrix = mats[[DatasetID]][[CombnNum]]
      )
    }
  )
}

We can compare this property against some of the other properties.

Uninvadability versus whether a community was found via assembly (“on Island 1”):

with(communitiesAll,
     table(Uninvadable, Islands1))
           Islands1
Uninvadable FALSE TRUE
      FALSE    20    0
      TRUE     32   30

Never invadable and assembled (good!), but about half of uninvadables are found without being assembled. What about of those that persist?

with(communitiesAll %>% dplyr::filter(Persists),
     table(Uninvadable, Islands1))
           Islands1
Uninvadable FALSE TRUE
      FALSE     8    0
      TRUE      0   30

Which of course fills in some of the blanks. So none of the communities that persist are uninvadable if they were not an end state of the assembly process.

Presence of Mass Effects

We check to see what happens when we treat each community as a pool for the other and perform assembly. Are the results the same as the diffusion system?

First, update the pairings.

communitiesAll <- communitiesAll %>% dplyr::group_by(
  CombnNum, Basals, Consumers, Dataset, DatasetID, TotalID
) %>% dplyr::mutate(
  OtherSteadyStates = dplyr::n() - 1
) %>% dplyr::ungroup()

This procedure can be done in two ways: first by directed invasion where one community is a pool for the other, and second with mutual (undirected) invasion where both communities are simultaneously pools for and invaded by each other. Note that in the directed case, we do not need to do any of the communities already marked as uninvadable with respect to the regional pools. The other communities they would be compared with are subsets of the regional pools, and so would already be checked against.

Indirect Mutualism

Here, we check to see if the networks created by each community (hybrid or otherwise) has mutualism embedded in it.

LS0tDQp0aXRsZTogIkFuc3dlcmluZyBRdWVzdGlvbnM7IEdhdGhlciBEYXRhLCAyMDIxLTA1Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KLS0tDQoNCmBgYHtyIGxpYnMsIG1lc3NhZ2U9RkFMU0V9DQojIENoZWNrIHJlcXVpc2l0ZSBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkLg0KcGFja2FnZXMgPC0gYygNCiAgInBsb3RseSIsIA0KICAiZHBseXIiLA0KICAiUk1UUkNvZGUyIg0KKQ0KZm9yIChwa2cgaW4gcGFja2FnZXMpIHsNCiAgbGlicmFyeShwa2csIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCn0NCg0KIyBSZXNlcnZlZCBOYW1lcw0KY2FuZGlkYXRlRGF0YSA8LSBOVUxMDQppc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3b1doaWNoIDwtIE5VTEwNCmlzbGFuZEludGVyYWN0aW9uc09uZVR3byA8LSBOVUxMDQppc2xhbmRJbnRlcmFjdGlvbnNPbmVUd29XaGljaCA8LSBOVUxMDQptYXRzIDwtIE5VTEwNCnBhcmFtRnJhbWUgPC0gTlVMTA0KcGxvdFNjYWxpbmdEYXRhIDwtIE5VTEwNCnBvb2xzIDwtIE5VTEwNCmBgYA0KDQojIERpc2VudGFuZ2xpbmcgRWZmZWN0cyBvbiB0aGUgVmlraW5nIERhdGEgey50YWJzZXR9DQoNCiMjIExvYWQgRGF0YQ0KYGBge3IgbG9hZERhdH0NCmVsbGlwc2lzQXBwbHkgPC0gZnVuY3Rpb24oLi4uLCBGVU4pIHsNCiAgbGFwcGx5KGFzLmxpc3QoLi4uKSwgRlVOKQ0KfQ0KDQpsb2FkKCJMTTE5OTYtTnVtUG9vbENvbS1RRGF0LTIwMjEtMDUuUkRhdGEiKQ0KIyBTdG9wIGlmIG5vdCBhbGwgYXJlIG5vdCBudWxsDQpzdG9waWZub3QoYWxsKHVubGlzdChlbGxpcHNpc0FwcGx5KA0KICBGVU4gPSBmdW5jdGlvbihib29sKSB7IWlzLm51bGwoYm9vbCl9LA0KICBjYW5kaWRhdGVEYXRhLCANCiAgaXNsYW5kSW50ZXJhY3Rpb25zT25lRW1wdHlUd28sDQogIGlzbGFuZEludGVyYWN0aW9uc09uZUVtcHR5VHdvV2hpY2gsDQogIGlzbGFuZEludGVyYWN0aW9uc09uZVR3bywNCiAgaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvV2hpY2gsDQogIG1hdHMsDQogIHBhcmFtRnJhbWUsDQogIHBsb3RTY2FsaW5nRGF0YSwNCiAgcG9vbHMNCikpKSkNCmBgYA0KDQpgYGB7ciB0ZXN0UGxvdH0NCnBsb3RTY2FsaW5nIDwtIHBsb3RseTo6cGxvdF9seSgNCiAgcGxvdFNjYWxpbmdEYXRhLA0KICB4ID0gfkJhc2FscywNCiAgeSA9IH5Db25zdW1lcnMsDQogIHogPSB+Q29tbXVuaXR5U2l6ZSwNCiAgY29sb3IgPSB+RGF0YXNldCwNCiAgY29sb3JzID0gYygicmVkIiwgImJsdWUiLCAiYmxhY2siKQ0KKQ0KDQpwbG90U2NhbGluZyA8LSBwbG90bHk6OmFkZF9tYXJrZXJzKHBsb3RTY2FsaW5nKQ0KDQpwbG90U2NhbGluZyA8LSBwbG90bHk6OmxheW91dCgNCiAgcGxvdFNjYWxpbmcsDQogIHNjZW5lID0gbGlzdCgNCiAgICB4YXhpcyA9IGxpc3QodHlwZSA9ICJsb2ciKSwNCiAgICB5YXhpcyA9IGxpc3QodHlwZSA9ICJsb2ciKSwNCiAgICBjYW1lcmEgPSBsaXN0KA0KICAgICAgZXllID0gbGlzdCgNCiAgICAgICAgeCA9IC0xLjI1LCB5ID0gLTEuMjUsIHogPSAuMDUNCiAgICAgICkNCiAgICApDQogICkNCikNCg0KcGxvdFNjYWxpbmcNCmBgYA0KDQoNCmBgYHtyIGNvbW11bml0aWVzQWxsU2FuaXR5Q2hlY2tzfQ0KIyBDaGVjayB0aGF0IHRoZSBUd28gaXNsYW5kIGFuZCBUaHJlZSBpc2xhbmQgc2NlbmFyaW9zIGFyZSBzZXQtdXAgdGhlIHNhbWUuDQpzdG9waWZub3QodW5saXN0KGxhcHBseShpc2xhbmRJbnRlcmFjdGlvbnNPbmVUd28sIGxlbmd0aCkpID09IA0KICAgICAgICAgICAgdW5saXN0KGxhcHBseShpc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3bywgbGVuZ3RoKSkpDQpzdG9waWZub3QobmFtZXMoaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvKSA9PSANCiAgICAgICAgICAgIG5hbWVzKGlzbGFuZEludGVyYWN0aW9uc09uZUVtcHR5VHdvKSkNCiMgQ2hlY2sgdGhhdCB0aGUgV2hpY2ggdmVyc2lvbnMgY29ycmVzcG9uZCBjb3JyZWN0bHkuDQpzdG9waWZub3QoDQogIHVubGlzdChsYXBwbHkoaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvV2hpY2gsIGZ1bmN0aW9uKHgpIHsNCiAgICBsZW5ndGgoUk1UUkNvZGUyOjpDc3ZSb3dTcGxpdCh4KSkNCiAgfSkpIA0KICA9PSB1bmxpc3QobGFwcGx5KGlzbGFuZEludGVyYWN0aW9uc09uZVR3bywgZnVuY3Rpb24oeCkgew0KICAgICMgV2UncmUgbGlrZSBvbmlvbnM7IHdlIGhhdmUgTEFZRVJTIQ0KICAgIGxhcHBseSh4LCBmdW5jdGlvbih5KSB7DQogICAgICBsYXBwbHkoeSwgZnVuY3Rpb24oeikgew0KICAgICAgICBzdW0oeiA+IDFFLTYpICMgSG93IG1hbnkgImxhcmdlIiBlbnRyaWVzIGFyZSB0aGVyZT8NCiAgICAgIH0pfSl9KSkNCikNCnN0b3BpZm5vdCgNCiAgdW5saXN0KGxhcHBseShpc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3b1doaWNoLCBmdW5jdGlvbih4KSB7DQogICAgbGVuZ3RoKFJNVFJDb2RlMjo6Q3N2Um93U3BsaXQoeCkpDQogIH0pKSANCiAgPT0gdW5saXN0KGxhcHBseShpc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3bywgZnVuY3Rpb24oeCkgew0KICAgICMgV2UncmUgbGlrZSBvbmlvbnM7IHdlIGhhdmUgTEFZRVJTIQ0KICAgIGxhcHBseSh4LCBmdW5jdGlvbih5KSB7DQogICAgICBsYXBwbHkoeSwgZnVuY3Rpb24oeikgew0KICAgICAgICBzdW0oeiA+IDFFLTYpICMgSG93IG1hbnkgImxhcmdlIiBlbnRyaWVzIGFyZSB0aGVyZT8NCiAgICAgIH0pfSl9KSkNCikNCmBgYA0KDQpgYGB7ciBjb21tdW5pdGllc0FsbEFkZEh5YnJpZHN9DQojIEh5YnJpZHMNCiMgQ3JlYXRlIGEgY291bnQgb2YgaG93IG1hbnkgdGltZXMgZWFjaCBlbnRyeSB3aWxsIGJlIHJlcGVhdGVkLg0KY29tbXVuaXRpZXNBbGxSZXBlYXRlciA8LSA1ICogdW5saXN0KGxhcHBseShpc2xhbmRJbnRlcmFjdGlvbnNPbmVUd28sIGxlbmd0aCkpDQoNCiMgQ3JlYXRlIHRlbXBsYXRlLg0KY29tbXVuaXRpZXNBbGwgPC0gZGF0YS5mcmFtZSgNCiAgQ29tYm5OdW0gPSByZXAoMCwgc3VtKGNvbW11bml0aWVzQWxsUmVwZWF0ZXIpKSwgIyBTaG91bGQgcmVwZWF0IGFsbCByb3dzLg0KICBCYXNhbHMgPSAwLA0KICBDb25zdW1lcnMgPSAwLA0KICBEYXRhc2V0ID0gIiIsDQogIERhdGFzZXRJRCA9IDAsDQogIENvbW11bml0aWVzID0gIiIsDQogIENvbW11bml0eVNpemUgPSAwLA0KICBPdGhlclN0ZWFkeVN0YXRlcyA9IDAsICMgVG8gYmUgcmVjYWxjdWxhdGVkDQogIENvbW11bml0eUFidW5kID0gIiIsDQogIENvbW11bml0eVByb2QgPSAwLA0KICBUb3RhbElEID0gIiIsDQogICMgQWRkaXRpb25hbCBDb2x1bW4hLCAxIGZvciBkaXJlY3QgYXNzZW1ibHksIDAgdW51c2VkLg0KICBJc2xhbmRzVXNlZCA9IHJlcChjKDIsMiwzLDMsMyksIHN1bShjb21tdW5pdGllc0FsbFJlcGVhdGVyKS81KQ0KKQ0KDQojIFJldHJpZXZlIHRoZSByb3dzIHVzZWQgdG8gbWFrZSBoeWJyaWRzDQpjb21tdW5pdGllc0FsbFByb3NwZWN0cyA8LSBjYW5kaWRhdGVEYXRhICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogIENvbWJuTnVtLCBCYXNhbHMsIENvbnN1bWVycywgRGF0YXNldCwgRGF0YXNldElELCBUb3RhbElEDQopICU+JSBkcGx5cjo6c2VsZWN0KA0KICBDb21ibk51bTpEYXRhc2V0SUQsIFRvdGFsSUQNCikgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogIENvdW50ID0gZHBseXI6Om4oKSwgLmdyb3VwcyA9ICJrZWVwIg0KKSAlPiUgZHBseXI6OmZpbHRlcigNCiAgQ291bnQgPiAxDQopICU+JSBkcGx5cjo6c2VsZWN0KA0KICAtQ291bnQNCikgJT4lIGRwbHlyOjphcnJhbmdlKA0KICBEYXRhc2V0SUQsIENvbWJuTnVtDQopDQoNCiMgTWFrZSBzdXJlIHRoYXQgdGhlIG5hbWVzIG1hdGNoLg0Kc3RvcGlmbm90KGNvbW11bml0aWVzQWxsUHJvc3BlY3RzJFRvdGFsSUQgPT0gbmFtZXMoY29tbXVuaXRpZXNBbGxSZXBlYXRlcikpDQoNCiMgSW5zZXJ0IHJlcGV0aXRpb25zLg0KY29tbXVuaXRpZXNBbGwkQ29tYm5OdW0gPC0gcmVwKGNvbW11bml0aWVzQWxsUHJvc3BlY3RzJENvbWJuTnVtLCBjb21tdW5pdGllc0FsbFJlcGVhdGVyKQ0KY29tbXVuaXRpZXNBbGwkQmFzYWxzIDwtIHJlcChjb21tdW5pdGllc0FsbFByb3NwZWN0cyRCYXNhbHMsIGNvbW11bml0aWVzQWxsUmVwZWF0ZXIpDQpjb21tdW5pdGllc0FsbCRDb25zdW1lcnMgPC0gcmVwKGNvbW11bml0aWVzQWxsUHJvc3BlY3RzJENvbnN1bWVycywgY29tbXVuaXRpZXNBbGxSZXBlYXRlcikNCmNvbW11bml0aWVzQWxsJERhdGFzZXQgPC0gcmVwKGNvbW11bml0aWVzQWxsUHJvc3BlY3RzJERhdGFzZXQsIGNvbW11bml0aWVzQWxsUmVwZWF0ZXIpDQpjb21tdW5pdGllc0FsbCREYXRhc2V0SUQgPC0gcmVwKGNvbW11bml0aWVzQWxsUHJvc3BlY3RzJERhdGFzZXRJRCwgY29tbXVuaXRpZXNBbGxSZXBlYXRlcikNCmNvbW11bml0aWVzQWxsJFRvdGFsSUQgPC0gcmVwKGNvbW11bml0aWVzQWxsUHJvc3BlY3RzJFRvdGFsSUQsIGNvbW11bml0aWVzQWxsUmVwZWF0ZXIpDQoNCiMgVG8gbW92ZSBvdmVyIGZyb20gdGhlIGRhdGEuDQojIENvbW11bml0aWVzID0gIiIsDQojIENvbW11bml0eUFidW5kID0gIiINCg0KY29tbXVuaXRpZXNBbGxbY29tbXVuaXRpZXNBbGwkSXNsYW5kc1VzZWQgPT0gMiwgXSRDb21tdW5pdGllcyA8LSANCiAgaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvV2hpY2gNCmNvbW11bml0aWVzQWxsW2NvbW11bml0aWVzQWxsJElzbGFuZHNVc2VkID09IDMsIF0kQ29tbXVuaXRpZXMgPC0gDQogIGlzbGFuZEludGVyYWN0aW9uc09uZUVtcHR5VHdvV2hpY2gNCg0KY29tbXVuaXRpZXNBbGxbY29tbXVuaXRpZXNBbGwkSXNsYW5kc1VzZWQgPT0gMiwgXSRDb21tdW5pdHlBYnVuZCA8LSANCiAgIyBXZSdyZSBsaWtlIG9uaW9uczsgd2UgaGF2ZSBMQVlFUlMhDQogIHVubGlzdChsYXBwbHkoaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvLCBmdW5jdGlvbih4KSB7DQogICAgbGFwcGx5KHgsIGZ1bmN0aW9uKHkpIHsNCiAgICAgIGxhcHBseSh5LCBmdW5jdGlvbih6KSB7DQogICAgICAgIHRvU3RyaW5nKHpbeiA+IDFFLTZdKQ0KICAgICAgfSkNCiAgICB9KQ0KICB9KSkNCiAgDQpjb21tdW5pdGllc0FsbFtjb21tdW5pdGllc0FsbCRJc2xhbmRzVXNlZCA9PSAzLCBdJENvbW11bml0eUFidW5kIDwtIA0KICB1bmxpc3QobGFwcGx5KGlzbGFuZEludGVyYWN0aW9uc09uZUVtcHR5VHdvLCBmdW5jdGlvbih4KSB7DQogICAgbGFwcGx5KHgsIGZ1bmN0aW9uKHkpIHsNCiAgICAgIGxhcHBseSh5LCBmdW5jdGlvbih6KSB7DQogICAgICAgIHRvU3RyaW5nKHpbeiA+IDFFLTZdKQ0KICAgICAgfSkNCiAgICB9KQ0KICB9KSkNCg0KIyBUbyBjYWxjdWxhdGUgZnJvbSB0aGUgZGF0YS4NCiMgQ29tbXVuaXR5U2l6ZSA9IDAsICMgVG8gYmUgY2FsY3VsYXRlZCBmcm9tIENvbW11bml0aWVzLg0KIyBPdGhlclN0ZWFkeVN0YXRlcyA9IDAsICMgVG8gYmUgcmVjYWxjdWxhdGVkIGxhc3QgYWZ0ZXIgZmlsdGVyaW5nDQojIENvbW11bml0eVByb2QgPSAwLCAjIFRvIGJlIHJlY2FsY3VsYXRlZCBhZnRlciBBYnVuZCBzdG9yZWQuDQpjb21tdW5pdGllc0FsbCRDb21tdW5pdHlTaXplIDwtIHVubGlzdChsYXBwbHkoDQogIGNvbW11bml0aWVzQWxsJENvbW11bml0aWVzLCBmdW5jdGlvbih4KSB7DQogICAgbGVuZ3RoKFJNVFJDb2RlMjo6Q3N2Um93U3BsaXQoeCkpDQogIH0pKSANCg0KZm9yIChyIGluIDE6bnJvdyhjb21tdW5pdGllc0FsbCkpIHsNCiAgY29tbXVuaXRpZXNBbGwkQ29tbXVuaXR5UHJvZFtyXSA8LSB3aXRoKA0KICAgIGNvbW11bml0aWVzQWxsW3IsIF0sIA0KICAgIFJNVFJDb2RlMjo6UHJvZHVjdGl2aXR5KA0KICAgICAgUG9vbCA9IHBvb2xzW1tEYXRhc2V0SURdXVtbQ29tYm5OdW1dXSwgDQogICAgICBJbnRlcmFjdGlvbk1hdHJpeCA9IG1hdHNbW0RhdGFzZXRJRF1dW1tDb21ibk51bV1dLCANCiAgICAgIENvbW11bml0eSA9IENvbW11bml0aWVzLCANCiAgICAgIFBvcHVsYXRpb25zID0gQ29tbXVuaXR5QWJ1bmQNCiAgICApDQogICkNCn0NCmBgYA0KDQpgYGB7ciBjb21tdW5pdGllc0FsbEFkZE9yaWdpbmFsc30NCiMgT3JpZ2luYWwgc3lzdGVtcw0KY29tbXVuaXRpZXNBbGwgPC0gcmJpbmQoDQogIGNhbmRpZGF0ZURhdGEgJT4lIGRwbHlyOjpzZWxlY3QoDQogICAgLUNvbW11bml0eUZyZXEsIC1Db21tdW5pdHlTZXENCiAgKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgICBJc2xhbmRzVXNlZCA9IDENCiAgKSwgDQogIGNvbW11bml0aWVzQWxsDQopDQoNCmBgYA0KDQpgYGB7ciBjb21tdW5pdGllc0FsbEhhc2h9DQojIFRyZWF0aW5nIHRoZSBQcm9kdWN0aXZpdHkgbGlrZSBvbmUgbWlnaHQgdHJlYXQgYSBoYXNoLA0KIyBpZiB0d28gcm93cyB3aXRoIHRoZSBzYW1lIHByb3BlcnRpZXMgYXJlIGFzc2lnbmVkIHRoZSBzYW1lIGhhc2gsIA0KIyB3ZSBvbmx5IGtlZXAgb25lLiANCiMgT25lIGRlY2ltYWwgcGxhY2UgbWlnaHQgYmUgZXhjZXNzaXZlLCANCiMgYnV0IHdlIGNhbiByZWZsZWN0IG9uIHRoYXQgaWYgcmVzdWx0cyBkb3duIHRoZSBsaW5lIGFyZSBub3QgaW50ZXJlc3RpbmcuDQojIEZvciB0aGUgcmVjb3JkIHRob3VnaCwgaXQgYXBwZWFycyB0aGF0IGl0IGlzIGEgZGVjZW50bHkgZ29vZCBhcHByb2FjaCBhdCANCiMgcmVtb3ZpbmcgZWZmZWN0aXZlbHkgbnVtZXJpY2FsIGR1cGxpY2F0ZXMuDQojIE5vdCBib3RoZXJpbmcsIHNvcnQgb2YsIHdpdGggSXNsYW5kc1VzZWQsIHNpbmNlIG1hbnkgdGltZXMgYSBjb21tdW5pdHkgaXMNCiMgcmVwcm9kdWNlZCBvbiB2YXJ5aW5nIG51bWJlcnMgb2YgaXNsYW5kcy4NCg0KIyBjb21tdW5pdGllc0FsbCA8LSBjb21tdW5pdGllc0FsbCAlPiUgZHBseXI6Om11dGF0ZSgNCiMgICB0ZW1wUHJvZCA9IHJvdW5kKENvbW11bml0eVByb2QsIDIpDQojICkgJT4lIGRwbHlyOjpkaXN0aW5jdCgNCiMgICBDb21ibk51bSwgQmFzYWxzLCBDb25zdW1lcnMsIERhdGFzZXQsIERhdGFzZXRJRCwgVG90YWxJRCwNCiMgICBDb21tdW5pdGllcywgQ29tbXVuaXR5U2l6ZSwgdGVtcFByb2QsIElzbGFuZHNVc2VkLA0KIyAgIC5rZWVwX2FsbCA9IFRSVUUNCiMgKSAlPiUgZHBseXI6OnNlbGVjdCgNCiMgICAtdGVtcFByb2QNCiMgKQ0KDQpjb21tdW5pdGllc0FsbCA8LSBjb21tdW5pdGllc0FsbCAlPiUgZHBseXI6Om11dGF0ZSgNCiAgdGVtcFByb2QgPSByb3VuZChDb21tdW5pdHlQcm9kLCAyKQ0KKSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICBDb21ibk51bSwgQmFzYWxzLCBDb25zdW1lcnMsIERhdGFzZXQsIERhdGFzZXRJRCwgVG90YWxJRCwNCiAgQ29tbXVuaXRpZXMsIENvbW11bml0eVNpemUsIHRlbXBQcm9kLA0KKSAlPiUgZHBseXI6OnN1bW1hcmlzZSgNCiAgQ29tbXVuaXR5QWJ1bmQgPSBkcGx5cjo6Zmlyc3QoQ29tbXVuaXR5QWJ1bmQpLA0KICBDb21tdW5pdHlQcm9kID0gZHBseXI6OmZpcnN0KENvbW11bml0eVByb2QpLA0KICBJc2xhbmRzVXNlZCA9IHRvU3RyaW5nKHVuaXF1ZShJc2xhbmRzVXNlZCkpLA0KICAuZ3JvdXBzID0gImRyb3AiDQopICU+JSBkcGx5cjo6c2VsZWN0KA0KICAtdGVtcFByb2QNCikgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgQ29tYm5OdW0sIEJhc2FscywgQ29uc3VtZXJzLCBEYXRhc2V0LCBEYXRhc2V0SUQsIFRvdGFsSUQNCikgJT4lIGRwbHlyOjptdXRhdGUoDQogIE90aGVyU3RlYWR5U3RhdGVzID0gZHBseXI6Om4oKSAtIDEsDQogIElzbGFuZHMxID0gZ3JlcGwocGF0dGVybiA9ICIxIiwgSXNsYW5kc1VzZWQsIGZpeGVkID0gVFJVRSkgIyBXaWxsIGJlIHVzZWZ1bA0KKQ0KDQpgYGANCg0KIyMgUGVyc2lzdGVuY2Ugb2YgSHlicmlkIENvbW11bml0aWVzDQpUaGUgaWRlYSBpcyBzdHJhaWdodGZvcndhcmQ6IGFmdGVyIGFsbG93aW5nIGludGVyYWN0aW9ucyBiZXR3ZWVuIGlzbGFuZHMsIGZvciBpc2xhbmRzIHRoYXQgYXJlIG5vdCB0aGUgc2FtZSBhcyBvbmUgb2YgdGhlIG9yaWdpbmFsIGNvbW11bml0aWVzLCBpc29sYXRlIHRoZSBpc2xhbmQgYW5kIGNoZWNrIHRvIHNlZSB3aGF0IGhhcHBlbnMuDQoNCmBgYHtyIGh5YnJpZHNPbmx5fQ0KY29tbXVuaXRpZXNIeWJyaWRzIDwtIGNvbW11bml0aWVzQWxsICU+JSBkcGx5cjo6ZmlsdGVyKA0KICAhSXNsYW5kczENCikgJT4lIGRwbHlyOjpzZWxlY3QoLUlzbGFuZHMxKQ0KYGBgDQoNCmBgYHtyIGFwcGx5RHluYW1pY3N9DQpjb21tdW5pdGllc0h5YnJpZHMkQWZ0ZXJTZXBBYnVuZCA8LSAiIg0KY29tbXVuaXRpZXNIeWJyaWRzJEFmdGVyU2VwQ29tbXVuaXR5IDwtICIiDQpjb21tdW5pdGllc0h5YnJpZHMkQWZ0ZXJTZXBDb21tdW5pdHlTaXplIDwtIDANCmNvbW11bml0aWVzSHlicmlkcyRBZnRlclNlcFByb2R1Y3Rpb24gPC0gMA0KZm9yIChyIGluIDE6bnJvdyhjb21tdW5pdGllc0h5YnJpZHMpKSB7DQogIHRlbXAgPC0gd2l0aCgNCiAgICBjb21tdW5pdGllc0h5YnJpZHNbciwgXSwNCiAgICB7ICAgIA0KICAgICAgdGVtcCA8LSBSTVRSQ29kZTI6OkNzdlJvd1NwbGl0KENvbW11bml0aWVzKQ0KICAgICAgUk1UUkNvZGUyOjpMYXdNb3J0b24xOTk2X051bUludGVncmF0aW9uKA0KICAgICAgICBBID0gbWF0c1tbRGF0YXNldElEXV1bW0NvbWJuTnVtXV1bdGVtcCwgdGVtcF0sDQogICAgICAgIFIgPSBwb29sc1tbRGF0YXNldElEXV1bW0NvbWJuTnVtXV0kUmVwcm9kdWN0aW9uUmF0ZVt0ZW1wXSwNCiAgICAgICAgWCA9IFJNVFJDb2RlMjo6Q3N2Um93U3BsaXQoQ29tbXVuaXR5QWJ1bmQpLCANCiAgICAgICAgT3V0ZXJUaW1lU3RlcFNpemUgPSAzRTQsDQogICAgICAgIFRvbGVyYW5jZSA9IDFFLTYNCiAgICAgICkgIyByZXRyaWV2ZSB0aGUgYWJ1bmRhbmNlIG92ZXIgdGltZSBtYXRyaXgNCiAgICB9DQogICkgDQogIA0KICB0ZW1wIDwtIHRlbXBbbnJvdyh0ZW1wKSwgLTFdICMgY2hvb3NlIGxhc3Qgcm93LCByZW1vdmUgdGltZSBjb2x1bW4uDQogIA0KICBjb21tdW5pdGllc0h5YnJpZHMkQWZ0ZXJTZXBDb21tdW5pdHlbcl0gPC0gdG9TdHJpbmcoDQogICAgUk1UUkNvZGUyOjpDc3ZSb3dTcGxpdChjb21tdW5pdGllc0h5YnJpZHMkQ29tbXVuaXRpZXNbcl0pW3doaWNoKHRlbXAgPiAxRS02KV0NCiAgKQ0KICANCiAgdGVtcCA8LSB0ZW1wW3doaWNoKHRlbXAgPiAxRS02KV0gIyByZW1vdmUgbWljcm9mb3hlcy4NCiAgDQogIGNvbW11bml0aWVzSHlicmlkcyRBZnRlclNlcEFidW5kW3JdIDwtIHRvU3RyaW5nKHRlbXApDQogIGNvbW11bml0aWVzSHlicmlkcyRBZnRlclNlcENvbW11bml0eVNpemVbcl0gPC0gbGVuZ3RoKHRlbXApDQogIA0KICBjb21tdW5pdGllc0h5YnJpZHMkQWZ0ZXJTZXBQcm9kdWN0aW9uW3JdIDwtIHdpdGgoDQogICAgY29tbXVuaXRpZXNIeWJyaWRzW3IsIF0sIA0KICAgIFJNVFJDb2RlMjo6UHJvZHVjdGl2aXR5KA0KICAgICAgUG9vbCA9IHBvb2xzW1tEYXRhc2V0SURdXVtbQ29tYm5OdW1dXSwgDQogICAgICBJbnRlcmFjdGlvbk1hdHJpeCA9IG1hdHNbW0RhdGFzZXRJRF1dW1tDb21ibk51bV1dLCANCiAgICAgIENvbW11bml0eSA9IEFmdGVyU2VwQ29tbXVuaXR5LCANCiAgICAgIFBvcHVsYXRpb25zID0gQWZ0ZXJTZXBBYnVuZA0KICAgICkNCiAgKQ0KfQ0KYGBgDQoNCmBgYHtyIGh5YnJpZHNQZXJzaXN0fQ0KY29tbXVuaXRpZXNIeWJyaWRzIDwtIGNvbW11bml0aWVzSHlicmlkcyAlPiUgZHBseXI6Om11dGF0ZSgNCiAgUGVyc2lzdHMgPSBBZnRlclNlcENvbW11bml0eSA9PSBDb21tdW5pdGllcywNCiAgUHJvZENoYW5nZSA9IEFmdGVyU2VwUHJvZHVjdGlvbiAtIENvbW11bml0eVByb2QNCikNCmBgYA0KDQpTbyBhZnRlciBydW5uaW5nIHRoZSBkeW5hbWljcyBmb3IgM0U0IHRpbWUgdW5pdHMgKGkuZS4gM3ggdGhlIGxlbmd0aCBvZiB0aW1lIHRoZSBkeW5hbWljcyBpbiB0aGUgbnVtZXJpY2FsIGFzc2VtYmx5IHJ1bnMgaW4gYmV0d2VlbiBhc3NlbWJseSBzdGVwcyBhbmQgMS41eCB0aGUgbGVuZ3RoIG9mIHRoZSBpc2xhbmQgZHluYW1pY3MpLCB0aGUgY29tbXVuaXRpZXMgdGhhdCBwZXJzaXN0IGFyZSBgciB3aGljaChjb21tdW5pdGllc0h5YnJpZHMkUGVyc2lzdHMpYC4NCkV4YW1pbmluZyB0aGUgY29tbXVuaXRpZXMgdGhlbXNlbHZlcywgdGhleSBhcmUgYWxsIHRoZSBzYW1lIGNvbW11bml0eSwgYWxiZWl0IHdpdGggZGlmZmVyZW50IHN0YXJ0aW5nIHBvaW50cy4NCmBgYHtyIGh5YnJpZHNQZXJzaXN0V2hpY2h9DQpjb21tdW5pdGllc0h5YnJpZHNbY29tbXVuaXRpZXNIeWJyaWRzJFBlcnNpc3RzLCBdDQpgYGANCg0KQW4gb2J2aW91cyBmb2xsb3ctdXAgcXVlc3Rpb246IGhvdyBtYW55IG9mIHRoZSBjb21tdW5pdGllcyB0aGF0IGNvbGxhcHNlIGRvIHNvIHRvIGNvbW11bml0aWVzIHRoYXQgd2UgaGF2ZSBub3QgYWxyZWFkeSBzZWVuPw0KDQpgYGB7ciBoeWJyaWRzQ29sbGFwc2VUb30NCmNvbW11bml0aWVzSHlicmlkcyA8LSBjb21tdW5pdGllc0h5YnJpZHMgJT4lIGRwbHlyOjptdXRhdGUoDQogIEFmdGVyU2VwQ29tbXVuaXR5QWxyZWFkeVByZXNlbnQgPSBBZnRlclNlcENvbW11bml0eSAlaW4lIGNvbW11bml0aWVzQWxsJENvbW11bml0aWVzDQopDQpzdW0oIWNvbW11bml0aWVzSHlicmlkcyRBZnRlclNlcENvbW11bml0eUFscmVhZHlQcmVzZW50KQ0KYGBgDQoNCkNvbnNvbGlkYXRpbmcgZG93biB0byB1bmlxdWUgZW5kaW5nIHN0YXRlcyB3ZSBoYXZlIHRoZSBmb2xsb3dpbmcuDQoNCmBgYHtyIGh5YnJpZHNDb2xsYXBzZVdoaWNofQ0KY29tbXVuaXRpZXNIeWJyaWRzWw0KICAhY29tbXVuaXRpZXNIeWJyaWRzJEFmdGVyU2VwQ29tbXVuaXR5QWxyZWFkeVByZXNlbnQsIA0KICBdICU+JSBkcGx5cjo6ZGlzdGluY3QoQ29tYm5OdW0sIEFmdGVyU2VwQ29tbXVuaXR5LCAua2VlcF9hbGwgPSBUUlVFKQ0KYGBgDQoNCldlIHdpbGwgYWRkIHRoZXNlIG5ldyBzdGF0ZXMgdG8gb3VyIGNhdGFsb2d1ZSBvZiBjb21tdW5pdGllcyBmcm9tIHRoZSBleHBlcmltZW50cy4NCldlIGFsc28gdGFrZSB0aGUgYWJ1bmRhbmNlIGFmdGVyIHNlcGFyYXRpb24gaWYgdGhlIGNvbW11bml0eSBwZXJzaXN0cyB0byBiZXR0ZXIgcmVmbGVjdCBzdGVhZHktc3RhdGUgY29uZGl0aW9ucy4NCg0KYGBge3IgaHlicmlkc1RvQWxsfQ0KY29tbXVuaXRpZXNBbGwgPC0gcmJpbmQoDQogIGNvbW11bml0aWVzQWxsICU+JSBkcGx5cjo6ZmlsdGVyKA0KICAgIElzbGFuZHMxID09IFRSVUUNCiAgKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgICBIeWJyaWRDb2xsYXBzZSA9IEZBTFNFLCBQZXJzaXN0cyA9IFRSVUUNCiAgKSwNCiAgY29tbXVuaXRpZXNIeWJyaWRzICU+JSBkcGx5cjo6bXV0YXRlKA0KICAgIENvbW11bml0eUFidW5kID0gaWZlbHNlKFBlcnNpc3RzLCBBZnRlclNlcEFidW5kLCBDb21tdW5pdHlBYnVuZCksDQogICAgSXNsYW5kczEgPSBGQUxTRSwgSHlicmlkQ29sbGFwc2UgPSBGQUxTRSwNCiAgKSAlPiUgZHBseXI6OnNlbGVjdCgNCiAgICAtQWZ0ZXJTZXBBYnVuZCwgLUFmdGVyU2VwQ29tbXVuaXR5LCAtQWZ0ZXJTZXBDb21tdW5pdHlTaXplLCANCiAgICAtQWZ0ZXJTZXBQcm9kdWN0aW9uLCAtUHJvZENoYW5nZSwgLUFmdGVyU2VwQ29tbXVuaXR5QWxyZWFkeVByZXNlbnQNCiAgKSAsDQogIHdpdGgoDQogICAgY29tbXVuaXRpZXNIeWJyaWRzWw0KICAgICAgIWNvbW11bml0aWVzSHlicmlkcyRBZnRlclNlcENvbW11bml0eUFscmVhZHlQcmVzZW50LCANCiAgICBdICU+JSBkcGx5cjo6ZGlzdGluY3QoQ29tYm5OdW0sIEFmdGVyU2VwQ29tbXVuaXR5LCAua2VlcF9hbGwgPSBUUlVFKSwNCiAgICBkYXRhLmZyYW1lKA0KICAgICAgQ29tYm5OdW0gPSBDb21ibk51bSwNCiAgICAgIEJhc2FscyA9IEJhc2FscywNCiAgICAgIENvbnN1bWVycyA9IENvbnN1bWVycywNCiAgICAgIERhdGFzZXQgPSBEYXRhc2V0LA0KICAgICAgRGF0YXNldElEID0gRGF0YXNldElELA0KICAgICAgVG90YWxJRCA9IFRvdGFsSUQsDQogICAgICBDb21tdW5pdGllcyA9IEFmdGVyU2VwQ29tbXVuaXR5LA0KICAgICAgQ29tbXVuaXR5U2l6ZSA9IEFmdGVyU2VwQ29tbXVuaXR5U2l6ZSwNCiAgICAgIENvbW11bml0eUFidW5kID0gQWZ0ZXJTZXBBYnVuZCwNCiAgICAgIENvbW11bml0eVByb2QgPSBBZnRlclNlcFByb2R1Y3Rpb24sDQogICAgICBJc2xhbmRzVXNlZCA9IElzbGFuZHNVc2VkLA0KICAgICAgT3RoZXJTdGVhZHlTdGF0ZXMgPSAwLA0KICAgICAgSXNsYW5kczEgPSBGQUxTRSwNCiAgICAgIEh5YnJpZENvbGxhcHNlID0gVFJVRSwNCiAgICAgIFBlcnNpc3RzID0gVFJVRSwNCiAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KICAgICkpDQopDQpgYGANCg0KIyMgSW52YWRhYmlsaXR5IG9mIEh5YnJpZCBDb21tdW5pdGllcw0KTG9va2luZyBhdCBhIGxvbmdlciB0aW1lIHNjYWxlLCB3aGF0IGhhcHBlbnMgaWYvd2hlbiBpbnZhc2lvbnMgcmVzdW1lPyBEbyB0aGUgaHlicmlkIGNvbW11bml0aWVzIHRoYXQgZW1lcmdlZCByZXRhaW4gdGhlIHVuaW52YWRhYmlsaXR5IG9mIHRoZSBwYXJlbnQgY29tbXVuaXRpZXM/DQoNClRoaXMgcXVlc3Rpb24gc2hvdWxkIGJlIHN0cmFpZ2h0Zm9yd2FyZCBhcyBpdCBpcyB0ZXN0aW5nIGEgc3RlcCBmcm9tIHRoZSBhc3NlbWJseSBwcm9jZXNzLg0KYGBge3IgYWxsQ29tbXVuaXRpZXNJbnZhZGFibGV9DQpjb21tdW5pdGllc0FsbCRVbmludmFkYWJsZSA8LSBOQQ0KZm9yIChyIGluIDE6bnJvdyhjb21tdW5pdGllc0FsbCkpIHsNCiAgY29tbXVuaXRpZXNBbGwkVW5pbnZhZGFibGVbcl0gPC0gd2l0aCgNCiAgICBjb21tdW5pdGllc0FsbFtyLCBdLA0KICAgIHsNCiAgICAgIHRlbXBSb3cgPC0gcmVwKE5BLCBucm93KHBvb2xzW1tEYXRhc2V0SURdXVtbQ29tYm5OdW1dXSkgKyAxKQ0KICAgICAgdGVtcFJvd1tSTVRSQ29kZTI6OkNzdlJvd1NwbGl0KENvbW11bml0aWVzKSArIDFdIDwtIA0KICAgICAgICBSTVRSQ29kZTI6OkNzdlJvd1NwbGl0KENvbW11bml0eUFidW5kKQ0KICAgICAgUk1UUkNvZGUyOjpMYXdNb3J0b24xOTk2X0NoZWNrVW5pbnZhZGFibGUoDQogICAgICAgIEFidW5kYW5jZVJvdyA9IHRlbXBSb3csDQogICAgICAgIFBvb2wgPSBwb29sc1tbRGF0YXNldElEXV1bW0NvbWJuTnVtXV0sDQogICAgICAgIENvbW11bml0eU1hdHJpeCA9IG1hdHNbW0RhdGFzZXRJRF1dW1tDb21ibk51bV1dDQogICAgICApDQogICAgfQ0KICApDQp9DQpgYGANCg0KV2UgY2FuIGNvbXBhcmUgdGhpcyBwcm9wZXJ0eSBhZ2FpbnN0IHNvbWUgb2YgdGhlIG90aGVyIHByb3BlcnRpZXMuDQoNClVuaW52YWRhYmlsaXR5IHZlcnN1cyB3aGV0aGVyIGEgY29tbXVuaXR5IHdhcyBmb3VuZCB2aWEgYXNzZW1ibHkgKCJvbiBJc2xhbmQgMSIpOg0KYGBge3IgdGFibGVVbmludmFkYWJsZUlzbGFuZDF9DQp3aXRoKGNvbW11bml0aWVzQWxsLA0KICAgICB0YWJsZShVbmludmFkYWJsZSwgSXNsYW5kczEpKQ0KYGBgDQpOZXZlciBpbnZhZGFibGUgYW5kIGFzc2VtYmxlZCAoZ29vZCEpLCBidXQgYWJvdXQgaGFsZiBvZiB1bmludmFkYWJsZXMgYXJlIGZvdW5kIHdpdGhvdXQgYmVpbmcgYXNzZW1ibGVkLg0KV2hhdCBhYm91dCBvZiB0aG9zZSB0aGF0IHBlcnNpc3Q/DQpgYGB7ciB0YWJsZVVuaW52YWRhYmxlSXNsYW5kMVBlcnNpc3R9DQp3aXRoKGNvbW11bml0aWVzQWxsICU+JSBkcGx5cjo6ZmlsdGVyKFBlcnNpc3RzKSwNCiAgICAgdGFibGUoVW5pbnZhZGFibGUsIElzbGFuZHMxKSkNCmBgYA0KV2hpY2ggb2YgY291cnNlIGZpbGxzIGluIHNvbWUgb2YgdGhlIGJsYW5rcy4NClNvIG5vbmUgb2YgdGhlIGNvbW11bml0aWVzIHRoYXQgcGVyc2lzdCBhcmUgdW5pbnZhZGFibGUgaWYgdGhleSB3ZXJlIG5vdCBhbiBlbmQgc3RhdGUgb2YgdGhlIGFzc2VtYmx5IHByb2Nlc3MuDQoNCiMjIFByZXNlbmNlIG9mIE1hc3MgRWZmZWN0cw0KV2UgY2hlY2sgdG8gc2VlIHdoYXQgaGFwcGVucyB3aGVuIHdlIHRyZWF0IGVhY2ggY29tbXVuaXR5IGFzIGEgcG9vbCBmb3IgdGhlIG90aGVyIGFuZCBwZXJmb3JtIGFzc2VtYmx5LiBBcmUgdGhlIHJlc3VsdHMgdGhlIHNhbWUgYXMgdGhlIGRpZmZ1c2lvbiBzeXN0ZW0/DQoNCkZpcnN0LCB1cGRhdGUgdGhlIHBhaXJpbmdzLg0KYGBge3IgdXBkYXRlT3RoZXJTdGVhZHlTdGF0ZXN9DQpjb21tdW5pdGllc0FsbCA8LSBjb21tdW5pdGllc0FsbCAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICBDb21ibk51bSwgQmFzYWxzLCBDb25zdW1lcnMsIERhdGFzZXQsIERhdGFzZXRJRCwgVG90YWxJRA0KKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgT3RoZXJTdGVhZHlTdGF0ZXMgPSBkcGx5cjo6bigpIC0gMQ0KKSAlPiUgZHBseXI6OnVuZ3JvdXAoKQ0KYGBgDQoNClRoaXMgcHJvY2VkdXJlIGNhbiBiZSBkb25lIGluIHR3byB3YXlzOiBmaXJzdCBieSBkaXJlY3RlZCBpbnZhc2lvbiB3aGVyZSBvbmUgY29tbXVuaXR5IGlzIGEgcG9vbCBmb3IgdGhlIG90aGVyLCBhbmQgc2Vjb25kIHdpdGggbXV0dWFsICh1bmRpcmVjdGVkKSBpbnZhc2lvbiB3aGVyZSBib3RoIGNvbW11bml0aWVzIGFyZSBzaW11bHRhbmVvdXNseSBwb29scyBmb3IgYW5kIGludmFkZWQgYnkgZWFjaCBvdGhlci4gDQpOb3RlIHRoYXQgaW4gdGhlIGRpcmVjdGVkIGNhc2UsIHdlIGRvIG5vdCBuZWVkIHRvIGRvIGFueSBvZiB0aGUgY29tbXVuaXRpZXMgYWxyZWFkeSBtYXJrZWQgYXMgdW5pbnZhZGFibGUgd2l0aCByZXNwZWN0IHRvIHRoZSAqcmVnaW9uYWwqIHBvb2xzLg0KVGhlIG90aGVyIGNvbW11bml0aWVzIHRoZXkgd291bGQgYmUgY29tcGFyZWQgd2l0aCBhcmUgc3Vic2V0cyBvZiB0aGUgcmVnaW9uYWwgcG9vbHMsIGFuZCBzbyB3b3VsZCBhbHJlYWR5IGJlIGNoZWNrZWQgYWdhaW5zdC4NCg0KDQojIyBJbmRpcmVjdCBNdXR1YWxpc20NCkhlcmUsIHdlIGNoZWNrIHRvIHNlZSBpZiB0aGUgbmV0d29ya3MgY3JlYXRlZCBieSBlYWNoIGNvbW11bml0eSAoaHlicmlkIG9yIG90aGVyd2lzZSkgaGFzIG11dHVhbGlzbSBlbWJlZGRlZCBpbiBpdC4NCg==